//
//  YMUploadZip.swift
//  swift_review
//
//  Created by Andrew on 2024/12/15.
//

import UIKit
import CryptoKit

// 定义上传所需的参数结构体
struct UploadParams {
    let addID: String // Apple ID
    let shan: String // UUID
    let fileName: String // 文件名
    let fileSha256: String // 文件的 SHA256 加密值
    let secretKey: String // 加密使用的密钥，Base64 编码
    let fragmentCount: Int // 文件分片总数
    let fragmentSize: Int // 每个分片的大小
    let fileInfo: FileInfo // 文件信息

    // 文件信息结构体
    struct FileInfo {
        let fileSize: Int // 文件大小
        let fileMD5: String // 文件的 MD5 加密值（小写）
    }
}

class YMUploadZip {
    private var uploadURL: URL // 上传的服务器地址
    private var session: URLSession // 网络会话

    // 初始化方法，设置上传地址
    init(uploadURL: URL) {
        self.uploadURL = uploadURL
        self.session = URLSession(configuration: .default)
    }

    // 上传文件方法
    func uploadFile(filePath: String, params: UploadParams, token: String, completion: @escaping (Result<String, Error>) -> Void) {
        // 尝试加载文件数据
        guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
            completion(.failure(NSError(domain: "FileUploader", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法加载文件"])));
            return
        }

        let totalSize = fileData.count // 文件总大小
        let fragmentSize = params.fragmentSize // 每个分片的大小
        var currentFragment = 0 // 当前上传的分片索引

        // 定义上传下一个分片的方法
        func uploadNextFragment() {
            // 如果所有分片都上传完成，调用完成回调
            guard currentFragment < params.fragmentCount else {
                completion(.success("所有分片上传完成")) // 上传完成
                return
            }

            // 计算当前分片的起始位置和结束位置
            let fragmentStart = currentFragment * fragmentSize
            let fragmentEnd = min(fragmentStart + fragmentSize, totalSize)
            let fragmentData = fileData[fragmentStart..<fragmentEnd] // 当前分片的数据

            var request = URLRequest(url: uploadURL)
            request.httpMethod = "POST" // 设置 HTTP 请求方法为 POST

            let boundary = "Boundary-\(UUID().uuidString)" // 定义 multipart 请求的分隔符
            request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") // 添加 token 到请求头

            // 创建 HTTP 请求的 body 数据
            let body = createMultipartBody(fragmentData: fragmentData, boundary: boundary, params: params, fragmentIndex: currentFragment)
            request.httpBody = body

            // 创建网络任务上传当前分片
            let task = session.dataTask(with: request) { data, response, error in
                if let error = error {
                    completion(.failure(error)) // 上传失败
                    return
                }

                // 检查服务器响应状态
                guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
                    completion(.failure(NSError(domain: "FileUploader", code: -2, userInfo: [NSLocalizedDescriptionKey: "服务器错误"])))
                    return
                }

                // 上传下一个分片
                currentFragment += 1
                uploadNextFragment()
            }

            task.resume() // 开始任务
        }

        // 开始上传第一个分片
        uploadNextFragment()
    }

    // 创建 multipart 表单的 body 数据
    private func createMultipartBody(fragmentData: Data, boundary: String, params: UploadParams, fragmentIndex: Int) -> Data {
        var body = Data()

        // 添加参数部分
        let paramsDict: [String: String] = [
            "addID": params.addID,
            "shan": params.shan,
            "fileName": params.fileName,
            "fileSha256": params.fileSha256,
            "secretKey": params.secretKey,
            "fragmentCount": "\(params.fragmentCount)",
            "fragmentSize": "\(params.fragmentSize)",
            "fileSize": "\(params.fileInfo.fileSize)",
            "fileMD5": params.fileInfo.fileMD5,
            "fragmentIndex": "\(fragmentIndex)"
        ]

        for (key, value) in paramsDict {
            body.append("--\(boundary)\r\n".data(using: .utf8)!)
            body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
            body.append("\(value)\r\n".data(using: .utf8)!)
        }

        // 添加文件分片部分
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"fileFragment\"; filename=\"\(params.fileName)\"\r\n".data(using: .utf8)!)
        body.append("Content-Type: application/octet-stream\r\n\r\n".data(using: .utf8)!)
        body.append(fragmentData)
        body.append("\r\n".data(using: .utf8)!)

        // 添加结束标志
        body.append("--\(boundary)--\r\n".data(using: .utf8)!)

        return body
    }
}

extension YMUploadZip {
    // 计算文件的 SHA256 哈希
    func calculateSHA256(for filePath: String) -> String? {
        guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
            print("无法加载文件数据")
            return nil
        }
        
        let hash = SHA256.hash(data: fileData) // 使用 CryptoKit 计算 SHA256
        return hash.compactMap { String(format: "%02x", $0) }.joined() // 转换为字符串
    }
    
    // 计算文件的 MD5 哈希值（小写）
    func calculateMD5(for filePath: String) -> String? {
        guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
            print("无法加载文件数据")
            return nil
        }
        
        let hash = Insecure.MD5.hash(data: fileData) // 使用 CryptoKit 计算 MD5
        return hash.map { String(format: "%02x", $0) }.joined() // 转换为小写字符串
    }
}
